package com.xyazm.order.statemachine.action.order.create.node;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xyazm.common.logger.LoggerFormat;
import com.xyazm.market.domain.dto.CalculateOrderAmountDTO;
import com.xyazm.market.domain.param.CalculateOrderAmountParam;
import com.xyazm.order.domain.param.CreateOrderParam;
import com.xyazm.order.enums.AmountTypeEnum;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.mapstruct.OrderApiMapstruct;
import com.xyazm.order.remote.MarketRemote;
import com.xyazm.order.remote.ProductRemote;
import com.xyazm.process.engine.process.ProcessContext;
import com.xyazm.process.engine.process.StandardProcessor;
import com.xyazm.product.domain.dto.ProductSkuDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 计算订单金额节点
 * @author xuyou
 * @date 2023年02月02日 21:11
 */
@Slf4j
@Component
public class CreateOrderCalculateAmountNode extends StandardProcessor {

    @Autowired
    private ProductRemote productRemote;
    @Autowired
    private MarketRemote marketRemote;

    @Override
    protected void processInternal(ProcessContext context) {
        CreateOrderParam createOrderParam = context.get("createOrderParam");
        // 获取商品详细信息
        List<ProductSkuDTO> productSkuList = listProductSkus(createOrderParam);
        // 计算订单金额
        CalculateOrderAmountDTO calculateOrderAmountDTO = calculateOrderAmount(createOrderParam, productSkuList);
        // 验证订单实付金额
        checkRealPayAmount(createOrderParam, calculateOrderAmountDTO);

        // 设置参数，传递给后面节点
        context.set("productSkuList", productSkuList);
        context.set("calculateOrderAmountDTO", calculateOrderAmountDTO);
    }


    /**
     * 获取商品详细信息
     * @author xuyou
     * 2022/4/20 0020 21:25
     * @param param
     */
    private List<ProductSkuDTO> listProductSkus(CreateOrderParam param) {
        List<CreateOrderParam.OrderItemParam> orderItemParamList = param.getOrderItemParamList();
        List<String> skuCodes = orderItemParamList.stream().map(CreateOrderParam.OrderItemParam::getSkuCode).collect(Collectors.toList());
        List<ProductSkuDTO> productSkuList = productRemote.listProductSkus(skuCodes,param.getSellerId());
        log.info(LoggerFormat.build()
                .remark("listProductSkus->return")
                .data("productSkus", productSkuList)
                .finish());
        return productSkuList;
    }


    /**
     * 计算订单金额
     * @author xuyou
     * 2022/4/26 0026 22:28
     * @param param
     * @param productSkuList
     */
    private CalculateOrderAmountDTO calculateOrderAmount(CreateOrderParam param, List<ProductSkuDTO> productSkuList) {
        // 转换param
        CalculateOrderAmountParam calculateOrderAmountParam =
                OrderApiMapstruct.INSTANCE.convertCalculateOrderAmountParam(param);
        // 将订单商品信息转成map
        Map<String, ProductSkuDTO> productSkuDTOMap = productSkuList.stream().collect(
                Collectors.toMap(ProductSkuDTO::getSkuCode, Function.identity()));
        // 订单条目补充商品信息
        List<CalculateOrderAmountParam.OrderItemParam> orderItemParamList = calculateOrderAmountParam.getOrderItemParamList();
        for (CalculateOrderAmountParam.OrderItemParam orderItemParam : orderItemParamList) {
            String skuCode = orderItemParam.getSkuCode();
            ProductSkuDTO productSkuDTO = productSkuDTOMap.get(skuCode);
            orderItemParam.setProductId(productSkuDTO.getProductId());
            orderItemParam.setSalePrice(productSkuDTO.getSalePrice());
        }
        // 设置区域id
        calculateOrderAmountParam.setRegionId(param.getOrderAddressParam().getRegionId());
        // 调用营销服务api,计算订单费用
        CalculateOrderAmountDTO calculateOrderAmountDTO = marketRemote.calculateOrderAmount(calculateOrderAmountParam);
        // 检查api调用结果
        if (calculateOrderAmountDTO == null) {
            throw new OrderBizException(OrderErrorCodeEnum.CALCULATE_ORDER_AMOUNT_ERROR);
        }
        // 订单费用信息
        List<CalculateOrderAmountDTO.OrderAmountDTO> orderAmountList = calculateOrderAmountDTO.getOrderAmountList();
        if (CollectionUtils.isEmpty(orderAmountList)) {
            throw new OrderBizException(OrderErrorCodeEnum.CALCULATE_ORDER_AMOUNT_ERROR);
        }
        // 订单条目费用明细
        List<CalculateOrderAmountDTO.OrderAmountDetailDTO> orderItemAmountList = calculateOrderAmountDTO.getOrderAmountDetail();
        if (CollectionUtils.isEmpty(orderItemAmountList)) {
            throw new OrderBizException(OrderErrorCodeEnum.CALCULATE_ORDER_AMOUNT_ERROR);
        }
        log.info(LoggerFormat.build()
                .remark("calculateOrderAmount->return")
                .data("return", calculateOrderAmountDTO)
                .finish());
        return calculateOrderAmountDTO;
    }


    /**
     * 验证订单实付金额
     * @author xuyou
     * 2022/4/29 0029 22:44
     * @param param 创建订单请求参数
     * @param calculateOrderAmountDTO 订单费用信息
     */
    private void checkRealPayAmount(CreateOrderParam param,CalculateOrderAmountDTO calculateOrderAmountDTO) {
        // 1.获取前端传的订单 实付金额
        List<CreateOrderParam.OrderAmountParam> orderAmountParamList = param.getOrderAmountParamList();
        // 将订单费用信息转成map key:费用类型
        Map<Integer, CreateOrderParam.OrderAmountParam> originOrderAmountMap = orderAmountParamList.stream().collect(
                Collectors.toMap(CreateOrderParam.OrderAmountParam::getAmountType, Function.identity()));

        Integer originRealPayAmount = originOrderAmountMap.get(AmountTypeEnum.REAL_PAY_AMOUNT.getCode()).getAmount();

        // 2.获取通过营促系统计算的订单 实付金额
        List<CalculateOrderAmountDTO.OrderAmountDTO> orderAmountDTOList = calculateOrderAmountDTO.getOrderAmountList();
        // 将订单费用信息转成map key:费用类型
        Map<Integer, CalculateOrderAmountDTO.OrderAmountDTO> orderAmountMap =
                orderAmountDTOList.stream().collect(Collectors.toMap(CalculateOrderAmountDTO.OrderAmountDTO::getAmountType, Function.identity()));
        Integer realPayAmount = orderAmountMap.get(AmountTypeEnum.REAL_PAY_AMOUNT.getCode()).getAmount();

        // 3.判断实付金额是否一致
        if (!originRealPayAmount.equals(realPayAmount)) {
            // 订单验价失败
            throw new OrderBizException(OrderErrorCodeEnum.ORDER_CHECK_REAL_PAY_AMOUNT_FAIL);
        }
    }
}
